#include "run_loop.h"

#include <windows.h>

#include <algorithm>

RunLoop::RunLoop() { }

RunLoop::~RunLoop() { }

void RunLoop::Run()
{
    bool keep_running = true;
    TimePoint next_flutter_event_time = TimePoint::clock::now();
    while (keep_running) {
        std::chrono::nanoseconds wait_duration = std::max(
            std::chrono::nanoseconds(0),
            next_flutter_event_time - TimePoint::clock::now());
        ::MsgWaitForMultipleObjects(
            0, nullptr, FALSE, static_cast<DWORD>(wait_duration.count() / 1000),
            QS_ALLINPUT);
        bool processed_events = false;
        MSG message;
        // All pending Windows messages must be processed;
        // MsgWaitForMultipleObjects won't return again for items left in the
        // queue after PeekMessage.
        while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) {
            processed_events = true;
            if (message.message == WM_QUIT) {
                keep_running = false;
                break;
            }
            ::TranslateMessage(&message);
            ::DispatchMessage(&message);
            // Allow Flutter to process messages each time a Windows message is
            // processed, to prevent starvation.
            next_flutter_event_time = std::min(next_flutter_event_time,
                                               ProcessFlutterMessages());
        }
        // If the PeekMessage loop didn't run, process Flutter messages.
        if (!processed_events) {
            next_flutter_event_time = std::min(next_flutter_event_time,
                                               ProcessFlutterMessages());
        }
    }
}

void RunLoop::RegisterFlutterInstance(flutter::FlutterEngine *flutter_instance)
{
    flutter_instances_.insert(flutter_instance);
}

void RunLoop::UnregisterFlutterInstance(flutter::FlutterEngine *flutter_instance)
{
    flutter_instances_.erase(flutter_instance);
}

RunLoop::TimePoint RunLoop::ProcessFlutterMessages()
{
    TimePoint next_event_time = TimePoint::max();
    for (auto instance : flutter_instances_) {
        std::chrono::nanoseconds wait_duration = instance->ProcessMessages();
        if (wait_duration != std::chrono::nanoseconds::max()) {
            next_event_time = std::min(next_event_time,
                                       TimePoint::clock::now() + wait_duration);
        }
    }
    return next_event_time;
}
